#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef _WIN32
#include <direct.h>
#include <io.h>
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif

// 全局变量
static volatile int running = 1;
static unsigned int interval = 5; // 默认%interval%秒
static char **program_args = NULL;
static int program_argc = 0;

// 信号处理函数
static void handle_signal(int sig) {
  (void)sig;
  running = 0;
  printf("\n[提示] 接收到退出信号，正在结束程序...\n");
}

// 从配置文件读取时间间隔
static void read_config_file() {
  FILE *fp = fopen("timelyrun.ini", "r");
  if (!fp) {
    printf("[提示] 未找到配置文件 timelyrun.ini，使用默认间隔 %u 秒\n",
           interval);
    return;
  }

  char line[256];
  while (fgets(line, sizeof(line), fp)) {
    // 去除换行符
    size_t len = strlen(line);
    if (len > 0 && line[len - 1] == '\n')
      line[len - 1] = '\0';

    // 跳过空行和注释
    if (line[0] == '#' || line[0] == ';' || line[0] == '\0')
      continue;

    // 查找等号分隔键值对
    char *equal_sign = strchr(line, '=');
    if (!equal_sign)
      continue;

    // 提取并清理键名和值
    *equal_sign = '\0';
    char *key = line;
    char *value = equal_sign + 1;

    // 去除键名前后空格
    while (*key && isspace((unsigned char)*key))
      key++;
    char *end = key + strlen(key) - 1;
    while (end > key && isspace((unsigned char)*end))
      end--;
    *(end + 1) = '\0';

    // 去除值前后空格
    while (*value && isspace((unsigned char)*value))
      value++;
    end = value + strlen(value) - 1;
    while (end > value && isspace((unsigned char)*end))
      end--;
    *(end + 1) = '\0';

    // 解析时间间隔
    if (strcmp(key, "interval") == 0) {
      // 验证是否为纯数字
      int is_valid = 1;
      for (int i = 0; value[i] != '\0'; i++) {
        if (!isdigit((unsigned char)value[i])) {
          is_valid = 0;
          break;
        }
      }

      if (is_valid && strlen(value) > 0) {
        unsigned int new_interval = (unsigned int)atoi(value);
        if (new_interval > 0) {
          interval = new_interval;
          printf("[提示] 从配置文件读取到时间间隔: %u 秒\n", interval);
        } else {
          printf("[警告] 配置文件中时间间隔必须大于0，使用默认值 %u 秒\n",
                 interval);
        }
      } else {
        printf("[警告] 配置文件中时间间隔格式无效，使用默认值 %u 秒\n",
               interval);
      }
    }
  }

  fclose(fp);
}

// ==================== Windows平台实现（优化版） ====================
#ifdef _WIN32
// 判断是否为CMD内置命令
static int is_builtin_command(const char *cmd) {
  const char *builtins[] = {"dir", "cd",   "echo",  "del", "copy", "move",
                            "ren", "type", "md",    "rd",  "cls",  "exit",
                            "set", "path", "title", "ver", "vol",  NULL};
  for (int i = 0; builtins[i]; i++) {
    if (strcmp(cmd, builtins[i]) == 0)
      return 1;
  }
  return 0;
}

// 在PATH中查找可执行文件（增强版：更全面的扩展名检查）
static char *find_executable(const char *cmd) {
  // 调试信息：显示正在查找的命令
  printf("[调试] 查找可执行文件: %s\n", cmd);

  // 1. 先检查绝对路径/相对路径
  if (strchr(cmd, '\\') || strchr(cmd, '/')) {
    // 检查文件是否存在
    if (_access(cmd, 0) == 0) {
      printf("[调试] 找到绝对路径/相对路径: %s\n", cmd);
      return strdup(cmd);
    }

    // 尝试添加常见扩展名
    const char *exts[] = {".exe", ".com", ".bat", ".cmd", ".sh", NULL};
    for (int i = 0; exts[i]; i++) {
      size_t len = strlen(cmd) + strlen(exts[i]) + 1;
      char *temp = (char *)malloc(len);
      sprintf(temp, "%s%s", cmd, exts[i]);
      if (_access(temp, 0) == 0) {
        printf("[调试] 找到带扩展名的路径: %s\n", temp);
        return temp;
      }
      free(temp);
    }
    printf("[调试] 未找到带扩展名的绝对路径/相对路径\n");
    return NULL;
  }

  // 2. 在PATH环境变量中查找
  char *path_env = getenv("PATH");
  if (!path_env) {
    printf("[调试] 未找到PATH环境变量\n");
    return NULL;
  }

  char *path_copy = strdup(path_env);
  char *token = strtok(path_copy, ";");
  char *full_path = NULL;
  const char *exts[] = {".exe", ".com", ".bat",
                        ".cmd", ".sh",  ""}; // 包含空扩展名

  while (token && !full_path) {
    // 跳过空路径
    if (strlen(token) == 0) {
      token = strtok(NULL, ";");
      continue;
    }

    // 尝试各种扩展名
    for (int i = 0; exts[i] && !full_path; i++) {
      size_t len = strlen(token) + strlen(cmd) + strlen(exts[i]) + 2;
      char *temp = (char *)malloc(len);
      sprintf(temp, "%s\\%s%s", token, cmd, exts[i]);

      if (_access(temp, 0) == 0) {
        full_path = temp;
        printf("[调试] 在PATH中找到: %s\n", full_path);
      } else {
        free(temp);
      }
    }

    token = strtok(NULL, ";");
  }

  if (!full_path) {
    printf("[调试] 未在PATH中找到: %s\n", cmd);
    printf("[调试] PATH内容: %s\n", path_env);
  }

  free(path_copy);
  return full_path;
}

// 执行命令
static int execute_command() {
  const char *command = (program_argc >= 1) ? program_args[0] : NULL;
  char *executable = NULL;
  char *cmd_line = NULL;
  LPWSTR cmd_line_w = NULL;
  PROCESS_INFORMATION pi = {0};
  int exit_code = 1;
  int argc = (program_argc >= 1) ? (program_argc - 1) : 0;
  char **args = (program_argc >= 2) ? (&program_args[1]) : NULL;

  // 边界检查
  if (!command) {
    fprintf(stderr, "[错误] 没有指定要执行的命令\n");
    goto cleanup;
  }

  // 1. 处理内置命令 vs 外部程序
  int is_builtin = is_builtin_command(command);
  if (!is_builtin) {
    executable = find_executable(command);
    if (!executable) {
      fprintf(stderr, "[错误] 找不到命令 '%s'（已在PATH中搜索常见扩展名）\n",
              command);
      goto cleanup;
    }
  }

  // 2. 计算命令行长度（增加调试信息）
  size_t cmd_line_len = 1;
  if (is_builtin) {
    cmd_line_len += strlen("cmd /c ") + strlen(command);
    printf("[调试] 内置命令，基础长度: %zu\n", cmd_line_len);
  } else {
    cmd_line_len += strlen(executable);
    printf("[调试] 外部程序，基础长度: %zu\n", cmd_line_len);
  }

  // 添加参数长度
  for (int i = 0; i < argc; i++) {
    cmd_line_len += strlen(args[i]) + 3; // 额外空间用于可能的引号
    printf("[调试] 参数 '%s' 增加长度: %zu\n", args[i], strlen(args[i]) + 3);
  }

  // 3. 分配命令行缓冲区
  cmd_line = (char *)malloc(cmd_line_len);
  if (!cmd_line) {
    fprintf(stderr, "[错误] 内存分配失败（命令行缓冲区）\n");
    goto cleanup;
  }
  cmd_line[0] = '\0';

  // 构建命令行内容
  if (is_builtin) {
    sprintf(cmd_line, "cmd /c %s", command);
    printf("[调试] 内置命令行: %s\n", cmd_line);
  } else {
    // 外部程序路径可能包含空格，需要用引号括起来
    if (strchr(executable, ' '))
      sprintf(cmd_line, "\"%s\"", executable);
    else
      strcpy(cmd_line, executable);
    printf("[调试] 外部程序命令行前缀: %s\n", cmd_line);
  }

  // 添加参数（带空格的参数需要用引号括起来）
  char *ptr = cmd_line + strlen(cmd_line);
  for (int i = 0; i < argc; i++) {
    *ptr++ = ' ';
    if (strchr(args[i], ' ')) {
      *ptr++ = '"';
      strcpy(ptr, args[i]);
      ptr += strlen(args[i]);
      *ptr++ = '"';
    } else {
      strcpy(ptr, args[i]);
      ptr += strlen(args[i]);
    }
  }
  *ptr = '\0';
  printf("[调试] 完整命令行: %s\n", cmd_line);

  // 4. 转换为宽字符
  int wlen = MultiByteToWideChar(CP_UTF8, 0, cmd_line, -1, NULL, 0);
  cmd_line_w = (LPWSTR)LocalAlloc(LPTR, wlen * sizeof(WCHAR));
  if (!cmd_line_w) {
    fprintf(stderr, "[错误] 转换命令行为宽字符失败\n");
    goto cleanup;
  }
  MultiByteToWideChar(CP_UTF8, 0, cmd_line, -1, cmd_line_w, wlen);

  // 5. 配置进程启动信息（确保输出正确显示）
  STARTUPINFOW si = {0};
  si.cb = sizeof(si);
  si.dwFlags = STARTF_USESTDHANDLES;
  si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);

  // 6. 创建进程
  BOOL create_success = CreateProcessW(NULL,       // 模块名
                                       cmd_line_w, // 命令行
                                       NULL,       // 进程安全属性
                                       NULL,       // 线程安全属性
                                       TRUE,       // 继承句柄
                                       0,          // 创建标志
                                       NULL,       // 环境变量
                                       NULL,       // 当前目录
                                       &si,        // 启动信息
                                       &pi         // 进程信息
  );

  if (!create_success) {
    DWORD err = GetLastError();
    fprintf(stderr, "[错误] 创建进程失败 (错误码: %lu) - 命令: %s\n", err,
            cmd_line);
    // 错误码解释
    switch (err) {
    case ERROR_FILE_NOT_FOUND:
      fprintf(stderr, "[错误详情] 系统找不到指定的文件\n");
      break;
    case ERROR_PATH_NOT_FOUND:
      fprintf(stderr, "[错误详情] 系统找不到指定的路径\n");
      break;
    case ERROR_ACCESS_DENIED:
      fprintf(stderr, "[错误详情] 访问被拒绝\n");
      break;
    default:
      fprintf(stderr, "[错误详情] 请参考Windows错误码文档\n");
    }
    goto cleanup;
  }

  // 7. 等待命令执行完成
  printf("[执行状态] 命令正在执行...\n");
  DWORD wait_result = WaitForSingleObject(pi.hProcess, INFINITE);
  if (wait_result == WAIT_OBJECT_0) {
    GetExitCodeProcess(pi.hProcess, (LPDWORD)&exit_code);
    if (exit_code == 0)
      printf("[执行状态] 命令执行成功\n");
    else
      fprintf(stderr, "[执行状态] 命令执行完成，退出码: %d\n", exit_code);
  } else {
    fprintf(stderr, "[错误] 等待进程失败 (错误码: %lu)\n", GetLastError());
    TerminateProcess(pi.hProcess, 1);
  }

// 清理标签
cleanup:
  if (pi.hProcess != NULL)
    CloseHandle(pi.hProcess);
  if (pi.hThread != NULL)
    CloseHandle(pi.hThread);
  if (cmd_line_w != NULL)
    LocalFree(cmd_line_w);
  if (cmd_line != NULL)
    free(cmd_line);
  if (executable != NULL)
    free(executable);

  return 0; // 确保主循环继续
}

// ==================== Linux平台实现 ====================
#else
// 在PATH中查找可执行文件
static char *find_executable(const char *cmd) {
  if (strchr(cmd, '/')) {
    if (access(cmd, X_OK) == 0)
      return strdup(cmd);
    return NULL;
  }

  char *path_env = getenv("PATH");
  if (!path_env)
    return NULL;

  char *path_copy = strdup(path_env);
  char *token = strtok(path_copy, ":");
  char *full_path = NULL;

  while (token && !full_path) {
    size_t len = strlen(token) + strlen(cmd) + 2;
    char *temp = (char *)malloc(len);
    sprintf(temp, "%s/%s", token, cmd);

    if (access(temp, X_OK) == 0)
      full_path = temp;
    else
      free(temp);

    token = strtok(NULL, ":");
  }

  free(path_copy);
  return full_path;
}

// 执行命令
static int execute_command() {
  const char *command = (program_argc >= 1) ? program_args[0] : NULL;
  char *executable = NULL;
  char **args = NULL;

  if (!command) {
    fprintf(stderr, "[错误] 没有指定要执行的命令\n");
    goto cleanup;
  }

  executable = find_executable(command);
  if (!executable) {
    fprintf(stderr, "[错误] 找不到命令 '%s'（已在PATH中搜索）\n", command);
    goto cleanup;
  }

  args = malloc((program_argc + 1) * sizeof(char *));
  if (!args) {
    perror("[错误] 内存分配失败（参数列表）");
    goto cleanup;
  }

  args[0] = executable;
  for (int i = 1; i < program_argc; i++)
    args[i] = program_args[i];
  args[program_argc] = NULL;

  pid_t pid = fork();
  if (pid == -1) {
    perror("[错误] fork失败");
    goto cleanup;
  } else if (pid == 0) {
    execvp(executable, args);
    perror("[错误] 执行命令失败");
    exit(1);
  } else {
    printf("[执行状态] 命令正在执行...\n");
    int status;
    waitpid(pid, &status, 0);

    if (WIFEXITED(status)) {
      int exit_code = WEXITSTATUS(status);
      if (exit_code == 0)
        printf("[执行状态] 命令执行成功\n");
      else
        fprintf(stderr, "[执行状态] 命令执行完成，退出码: %d\n", exit_code);
    } else {
      fprintf(stderr, "[执行状态] 命令被异常终止\n");
    }
  }

cleanup:
  if (args != NULL)
    free(args);
  if (executable != NULL)
    free(executable);

  return 0;
}
#endif

// 打印格式化时间和命令信息
static void print_current_time() {
  time_t now;
  struct tm *timeinfo;
  char time_str[64];

  time(&now);
  timeinfo = localtime(&now);
  strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", timeinfo);

  printf("\n======================================");
  printf("\n[执行时间] %s", time_str);
  printf("\n[执行命令] ");
  for (int i = 0; i < program_argc; i++)
    printf("%s ", program_args[i]);
  printf("\n======================================\n");
}

// 显示使用帮助
static void print_usage(const char *prog_name) {
  fprintf(stderr, "timelyrun - 定时执行命令工具\n");
  fprintf(stderr, "用法: %s <要执行的程序> [程序参数...]\n", prog_name);
  fprintf(stderr, "配置文件: timelyrun.ini （格式：interval=秒数，默认%d秒）\n",
          interval);
  fprintf(stderr, "示例:\n");
  fprintf(stderr, "  %s gcc --version          # 定时查看gcc版本\n", prog_name);
  fprintf(stderr, "  %s dir /w                 # 定时列出目录（宽格式）\n",
          prog_name);
  fprintf(stderr, "  %s ping www.baidu.com -n 4 # 定时ping百度\n", prog_name);
}

int main(int argc, char **argv) {
  if (argc < 2) {
    print_usage(argv[0]);
    return 1;
  }

  read_config_file();
  program_args = argv + 1;
  program_argc = argc - 1;

  // 注册信号处理
  signal(SIGINT, handle_signal);
  signal(SIGTERM, handle_signal);
#ifdef _WIN32
  signal(SIGBREAK, handle_signal);
#else
  signal(SIGHUP, handle_signal);
  signal(SIGQUIT, handle_signal);
#endif

  // 显示启动信息
  printf("======================================\n");
  printf("timelyrun 已启动\n");
  printf("执行间隔: %u 秒\n", interval);
  printf("执行命令: ");
  for (int i = 0; i < program_argc; i++)
    printf("%s ", program_args[i]);
  printf("\n退出方式: 按 Ctrl+C\n");
  printf("======================================\n");

  // 主循环
  while (running) {
    print_current_time();
    execute_command();

    if (running) {
      printf("\n[等待中] 将于 %u 秒后再次执行...\n", interval);
#ifdef _WIN32
      Sleep(interval * 1000);
#else
      unsigned int remaining = interval;
      while (remaining > 0 && running)
        remaining = sleep(remaining);
#endif
    }
  }

  printf("[提示] 程序已正常退出\n");
  return 0;
}
